/*---------------------------------------------------------------------
 * File name: hal_timer.cpp
 *
 * Copyright (c) <2020-2022>, <ChenLong>
 *
 * All rights reserved.
 *
 * Author: ChenLong
 * Email: worldlong@foxmail.com
 *--------------------------------------------------------------------*/
#include "stm32g4xx.h"
#include "hal_timer.h"
#include "hal_config.h"
#include "hal_system.h"
#include "hal_def.h"
#if defined(USING_RT_THREAD)
#include "rtthread.h" 
#endif
/*
*/
static void (*irq_hook[12])(void *param, uint32_t status) = {nullptr};
static void *irq_hook_param[12]={nullptr};

#if TIM1_ENABLE
static uint64_t timer1_update_counter = 0;
#endif
#if TIM2_ENABLE
static uint64_t timer2_update_counter = 0;
#endif
#if TIM3_ENABLE
static uint64_t timer3_update_counter = 0;
#endif
#if TIM4_ENABLE
static uint64_t timer4_update_counter = 0;
#endif
#if TIM5_ENABLE
static uint64_t timer5_update_counter = 0;
#endif
#if TIM6_ENABLE
static uint64_t timer6_update_counter = 0;
#endif
#if TIM7_ENABLE
static uint64_t timer7_update_counter = 0;
#endif
#if TIM8_ENABLE
static uint64_t timer8_update_counter = 0;
#endif
#if TIM15_ENABLE
static uint64_t timer15_update_counter = 0;
#endif
#if TIM16_ENABLE
static uint64_t timer16_update_counter = 0;
#endif
#if TIM17_ENABLE
static uint64_t timer17_update_counter = 0;
#endif
#if TIM20_ENABLE
static uint64_t timer20_update_counter = 0;
#endif
/*
*/
bool HAL_Timer::init(uint64_t cfg0)
{
  uint8_t nvic_irqn = 0;
  uint8_t nvic_prio = 0;
  
  cfg = cfg0;
  uint32_t tmp = HTIM_CFG_EXTRACT(cfg, PORT);
  if(tmp == 0) {  //No port
    return false;
  }
#if TIM1_ENABLE
  else if(tmp == HTIM_PORT(1)) {
    port = (uint32_t)TIM1;
    nvic_irqn = TIM1_UP_TIM16_IRQn;
    nvic_prio = NVIC_IRQ_TIM1_PRIORITY;
    _counter_bits = 16;
    _update_counter = &timer1_update_counter;
    _index = 0;
  } 
#endif
#if TIM2_ENABLE
  else if(tmp == HTIM_PORT(2)) {
    port = (uint32_t)TIM2;
    nvic_irqn = TIM2_IRQn;
    nvic_prio = NVIC_IRQ_TIM2_PRIORITY;
    _counter_bits = 32;
    _update_counter = &timer2_update_counter;
    _index = 1;
  } 
#endif
#if TIM3_ENABLE
  else if(tmp == HTIM_PORT(3)) {
    port = (uint32_t)TIM3;
    nvic_irqn = TIM3_IRQn;
    nvic_prio = NVIC_IRQ_TIM3_PRIORITY;
    _counter_bits = 16;
    _update_counter = &timer3_update_counter;
    _index = 2;
  } 
#endif
#if TIM4_ENABLE
  else if(tmp == HTIM_PORT(4)) {
    port = (uint32_t)TIM4;
    nvic_irqn = TIM4_IRQn;
    nvic_prio = NVIC_IRQ_TIM4_PRIORITY;
    _counter_bits = 16;
    _update_counter = &timer4_update_counter;
    _index = 3;
  } 
#endif
#if TIM5_ENABLE
  else if(tmp == HTIM_PORT(5)) {
    port = (uint32_t)TIM5;
    nvic_irqn = TIM5_IRQn;
    nvic_prio = NVIC_IRQ_TIM5_PRIORITY;
    _counter_bits = 32;
    _update_counter = &timer5_update_counter;
    _index = 4;
  } 
#endif
#if TIM6_ENABLE
  else if(tmp == HTIM_PORT(6)) {
    port = (uint32_t)TIM6;
    nvic_irqn = TIM6_DAC_IRQn;
    nvic_prio = NVIC_IRQ_TIM6_PRIORITY;
    _counter_bits = 16;
    _update_counter = &timer6_update_counter;
    _index = 5;
  } 
#endif
#if TIM7_ENABLE
  else if(tmp == HTIM_PORT(7)) {
    port = (uint32_t)TIM7;
    nvic_irqn = TIM7_DAC_IRQn;
    nvic_prio = NVIC_IRQ_TIM7_PRIORITY;
    _counter_bits = 16;
    _update_counter = &timer7_update_counter;
    _index = 6;
  } 
#endif
#if TIM8_ENABLE
  else if(tmp == HTIM_PORT(8)) {
    port = (uint32_t)TIM8;
    nvic_irqn = TIM8_UP_IRQn;
    nvic_prio = NVIC_IRQ_TIM8_PRIORITY;
    _counter_bits = 16;
    _update_counter = &timer8_update_counter;
    _index = 7;
  } 
#endif
#if TIM15_ENABLE
  else if(tmp == HTIM_PORT(15)) {
    port = (uint32_t)TIM15;
    nvic_irqn = TIM1_BRK_TIM15_IRQn;
    nvic_prio = NVIC_IRQ_TIM15_PRIORITY;
    _counter_bits = 16;
    _update_counter = &timer15_update_counter;
    _index = 8;
  } 
#endif
#if TIM16_ENABLE
  else if(tmp == HTIM_PORT(16)) {
    port = (uint32_t)TIM16;
    nvic_irqn = TIM1_UP_TIM16_IRQn;
    nvic_prio = NVIC_IRQ_TIM16_PRIORITY;
    _counter_bits = 16;
    _update_counter = &timer16_update_counter;
    _index = 9;
  } 
#endif
#if TIM17_ENABLE
  else if(tmp == HTIM_PORT(17)) {
    port = (uint32_t)TIM17;
    nvic_irqn = TIM1_TRG_COM_TIM17_IRQn;
    nvic_prio = NVIC_IRQ_TIM17_PRIORITY;
    _counter_bits = 16;
    _update_counter = &timer17_update_counter;
    _index = 10;
  } 
#endif
#if TIM20_ENABLE
  else if(tmp == HTIM_PORT(20)) {
    port = (uint32_t)TIM20;
    nvic_irqn = TIM20_UP_IRQn;
    nvic_prio = NVIC_IRQ_TIM20_PRIORITY;
    _counter_bits = 16;
    _update_counter = &timer20_update_counter;
    _index = 11;
  } 
#endif
  else if(port == 0){
    return false;
  }
          
  if(!config(cfg)) {
    return false;
  }
  
  tmp = HTIM_CFG_EXTRACT(cfg, INT);
  if(tmp == HTIM_INT(ENABLE) && nvic_irqn) {
    HAL_NVIC_SetPriority((IRQn_Type)nvic_irqn, nvic_prio, 0);
    HAL_NVIC_EnableIRQ((IRQn_Type)nvic_irqn);
    ((TIM_TypeDef *)port)->DIER |= BIT(0);   //Enable update interrupt
  } else {
    ((TIM_TypeDef *)port)->DIER &= ~BIT(0);  //Disable update interrupt
  }
  ((TIM_TypeDef *)port)->CR1 |= BIT(0);  //Enable counter
  
  return true;
}
/*
*/
bool HAL_Timer::config(uint64_t cfg0)
{  
  cfg &= ~HTIM_CFG_MASK;
  cfg |= cfg0 & HTIM_CFG_MASK;
  
  clock = (uint32_t)HTIM_CFG_EXTRACT(cfg, CLOCK);
  freq = (uint32_t)HTIM_CFG_EXTRACT(cfg, FREQ);
  
  if(freq >= clock) {
    return false;
  }
  
  period = 0xffffffff;
  if(freq > 0) {
    period = clock / freq - 1;
  }
  if(_counter_bits == 16 && period > 0xffff) {
    period = 0xffff;
  }
  
  //Attention, timer clock input must = SystemCoreClock
  uint32_t prescalar = SystemCoreClock / clock - 1;  
  if(prescalar > 0xffff) {
    return false;
  }
  
  TIM_HandleTypeDef htim;
  
  htim.Instance = (TIM_TypeDef*)port;
  htim.Init.Prescaler = prescalar;
  htim.Init.Period = period;
  uint32_t tmp = HTIM_CFG_EXTRACT(cfg, CTM);
  if(tmp == HTIM_CTM(UP)) {
    htim.Init.CounterMode = TIM_COUNTERMODE_UP;
    _counter_dir = 1;
  } else if(tmp == HTIM_CTM(DOWN)) {
    htim.Init.CounterMode = TIM_COUNTERMODE_DOWN;
    _counter_dir = -1;
  } else if(tmp == HTIM_CTM(CA1)) {
    htim.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1;
    _counter_dir = 0;
  } else if(tmp == HTIM_CTM(CA2)) {
    htim.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED2;
    _counter_dir = 0;
  } else if(tmp == HTIM_CTM(CA3)) {
    htim.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED3;
    _counter_dir = 0;
  } else {
    return false;
  }
  htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim.Init.RepetitionCounter = 0;
  htim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim) != HAL_OK)
  {
    return false;
  }
  htim.Instance->CNT = 0;  //clear
  htim.Instance->SR &= ~BIT(0);  //clear
  
  return true;
}
/*
*/
uint64_t HAL_Timer::ticks()
{
  if(!port || _counter_dir != 1) {
    return 0;
  }
  
  uint32_t sr = enter_critical();
  uint64_t ticks1 = ((TIM_TypeDef *)port)->CNT;
  uint32_t flag = ((TIM_TypeDef *)port)->SR;
  uint64_t ticks2 = ((TIM_TypeDef *)port)->CNT;
  if(flag & BIT(0)) {
    ticks1 = ticks2 + (1 << _counter_bits);
  }
  ticks1 += *_update_counter;
  exit_critical(sr);
  
  return ticks1;
}
/*
*/
uint64_t HAL_Timer::micros()
{
  uint64_t tmp = ticks();
  if(clock == 1000000) {
    return tmp;
  } else if(clock > 1000000) {
    return tmp / (clock / 1000000);
  } else {
    return tmp * (1000000 / clock);
  }
}
/*
*/
void HAL_Timer::attach_irq(void(*fun)(void *param, uint32_t status), void *param)
{
  irq_hook[_index] = fun;
  irq_hook_param[_index] = param;
  if(irq_hook[0]) {}        //just use to remove the Warning[Pe550]
  if(irq_hook_param[0]) {}  //just use to remove the Warning[Pe550]
}
/*************************************************
              IRQHandler
**************************************************/
#ifdef __cplusplus
 extern "C" {
#endif

//TIM1 and TIM16
#if TIM1_ENABLE || TIM16_ENABLE 
void TIM1_UP_TIM16_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  uint32_t status;
  //TIM1
#if TIM1_ENABLE
  status = TIM1->SR;
  if(status & BIT(0) != RESET) {
    TIM1->SR &= ~BIT(0);  //clear
    uint32_t sr = enter_critical();  //must disable interrupt
    timer1_update_counter += 1ll<<16;
    exit_critical(sr);
  }
  if(irq_hook[0] != nullptr) {
    irq_hook[0](irq_hook_param[0], status);
  }
#endif
    //TIM16
#if TIM16_ENABLE
  status = TIM16->SR;
  if(status & BIT(0) != RESET) {
    TIM16->SR &= ~BIT(0);  //clear
    uint32_t sr = enter_critical();  //must disable interrupt
    timer16_update_counter += 1ll<<16;
    exit_critical(sr);
  }
  if(irq_hook[9] != nullptr) {
    irq_hook[9](irq_hook_param[9], status);
  }
#endif
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif

//TIM2
#if TIM2_ENABLE 
void TIM2_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  
  uint32_t status = TIM2->SR;
  if(status & BIT(0) != RESET) {
    TIM2->SR &= ~BIT(0);  //clear
    uint32_t sr = enter_critical();  //must disable interrupt
    timer2_update_counter += 1ll<<32;
    exit_critical(sr);
  }
  if(irq_hook[1] != nullptr) {
    irq_hook[1](irq_hook_param[1], status);
  }

#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif

//TIM3
#if TIM3_ENABLE 
void TIM3_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  
  uint32_t status = TIM3->SR;
  if(status & BIT(0) != RESET) {
    TIM3->SR &= ~BIT(0);  //clear
    uint32_t sr = enter_critical();  //must disable interrupt
    timer3_update_counter += 1ll<<16;
    exit_critical(sr);
  }
  if(irq_hook[2] != nullptr) {
    irq_hook[2](irq_hook_param[2], status);
  }

#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif

//TIM4
#if TIM4_ENABLE 
void TIM4_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  
  uint32_t status = TIM4->SR;
  if(status & BIT(0) != RESET) {
    TIM4->SR &= ~BIT(0);  //clear
    uint32_t sr = enter_critical();  //must disable interrupt
    timer4_update_counter += 1ll<<16;
    exit_critical(sr);
  }
  if(irq_hook[3] != nullptr) {
    irq_hook[3](irq_hook_param[3], status);
  }

#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif

//TIM5
#if TIM5_ENABLE 
void TIM5_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  
  uint32_t status = TIM5->SR;
  if(status & BIT(0) != RESET) {
    TIM5->SR &= ~BIT(0);  //clear
    uint32_t sr = enter_critical();  //must disable interrupt
    timer5_update_counter += 1ll<<32;
    exit_critical(sr);
  }
  if(irq_hook[4] != nullptr) {
    irq_hook[4](irq_hook_param[4], status);
  }

#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif

//TIM6
#if TIM6_ENABLE 
void TIM6_DAC_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  
  uint32_t status = TIM6->SR;
  if(status & BIT(0) != RESET) {
    TIM6->SR &= ~BIT(0);  //clear
    uint32_t sr = enter_critical();  //must disable interrupt
    timer6_update_counter += 1ll<<16;
    exit_critical(sr);
  }
  if(irq_hook[5] != nullptr) {
    irq_hook[5](irq_hook_param[5], status);
  }

#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
//TIM7
#if TIM7_ENABLE 
void TIM7_DAC_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  
  uint32_t status = TIM7->SR;
  if(status & BIT(0) != RESET) {
    TIM7->SR &= ~BIT(0);  //clear
    uint32_t sr = enter_critical();  //must disable interrupt
    timer7_update_counter += 1ll<<16;
    exit_critical(sr);
  }
  if(irq_hook[6] != nullptr) {
    irq_hook[6](irq_hook_param[6], status);
  }

#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
//TIM8
#if TIM8_ENABLE 
void TIM8_UP_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  
  uint32_t status = TIM8->SR;
  if(status & BIT(0) != RESET) {
    TIM8->SR &= ~BIT(0);  //clear
    uint32_t sr = enter_critical();  //must disable interrupt
    timer8_update_counter += 1ll<<16;
    exit_critical(sr);
  }
  if(irq_hook[7] != nullptr) {
    irq_hook[7](irq_hook_param[7], status);
  }

#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
//TIM15
#if TIM15_ENABLE 
void TIM1_BRK_TIM15_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  
  uint32_t status = TIM15->SR;
  if(status & BIT(0) != RESET) {
    TIM15->SR &= ~BIT(0);  //clear
    uint32_t sr = enter_critical();  //must disable interrupt
    timer15_update_counter += 1ll<<16;
    exit_critical(sr);
  }
  if(irq_hook[8] != nullptr) {
    irq_hook[8](irq_hook_param[8], status);
  }

#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
//TIM17
#if TIM17_ENABLE 
void TIM1_TRG_COM_TIM17_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  
  uint32_t status = TIM17->SR;
  if(status & BIT(0) != RESET) {
    TIM17->SR &= ~BIT(0);  //clear
    uint32_t sr = enter_critical();  //must disable interrupt
    timer17_update_counter += 1ll<<16;
    exit_critical(sr);
  }
  if(irq_hook[10] != nullptr) {
    irq_hook[10](irq_hook_param[10], status);
  }

#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
//TIM20
#if TIM20_ENABLE 
void TIM20_UP_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  
  uint32_t status = TIM20->SR;
  if(status & BIT(0) != RESET) {
    TIM20->SR &= ~BIT(0);  //clear
    uint32_t sr = enter_critical();  //must disable interrupt
    timer20_update_counter += 1ll<<16;
    exit_critical(sr);
  }
  if(irq_hook[11] != nullptr) {
    irq_hook[11](irq_hook_param[11], status);
  }

#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
/*
*/
#ifdef __cplusplus
}
#endif
